#ifndef PROCESS_MANAGEMENT_H
#define PROCESS_MANAGEMENT_H

/* define that are needed in other modules */
#define NUMBER_MAX_PROCESSES 100
#define MAX_PRIORITY 42
#define STACK_SIZE 0x1000


/* the following define represents the system in the process tree. 
   A process can have SYSTEM_ID as a parent in two cases:
   - it has been created by the system itself
   - its parent has terminated
*/
#define SYSTEM_ID -1 

#include "types.h"
#include "pcb.h"
#include "scheduler.h"
#include "delay.h"
#include "user_programs.h"
#include "uart.h"
#include "process_supervision.h"
#include "dynamic_memory.h"

/* function declarations */

/* initializes the global array processes */
void initialize_processes_table();

/* creates a new process (with priority as priority level) by allocating a PCB to it and returns its pid. Returns a negative number in case of failure. This process executes the program named program_name */
int32_t create_process(uint32_t priority, char* program_name, int32_t parent,int32_t parameters[4]);

/* removes the PCB of the process pid. The function also passes the return value of the removed process to its parent (if it is waiting for). If the parent is not waiting for the termination of its child, the latter becomes a zombie whose PCB will only be deleted when:
   - the parent finally reads the return value
   - the parent terminates
*/
int32_t remove_process(int32_t pid, int32_t return_value);

/* removes the PCB pcb. The function also passes the return value of the removed process to its parent (if it is waiting for). If the parent is not waiting for the termination of its child, the latter becomes a zombie whose PCB will only be deleted when:
   - the parent finally reads the return value
   - the parent terminates
*/
int32_t remove_process_pcb(PCB* pcb, int32_t return_value);

/* marks pcb as unused and decrements count_processes */
void delete_pcb(PCB* pcb);

/* looks for pcb's children and changes their parents to SYSTEM_ID */
void adopt_children(PCB* pcb);

/* passes return_value of a process to pcb's parent if it is waiting for it. Otherwise, pcb becomes a zombie */
int32_t pass_return_value(PCB* pcb, int32_t return_value);

/* returns a pointer to the PCB with pid as pid */
PCB* get_process_by_pid(int32_t pid);

/* changes the priority of the process pid */
int32_t change_priority(int32_t pid, int32_t priority);

/* blocks the process pid. It is only possible to block a process ready or running (blocking a delayed process would not make much sense).
Returns 1 in case of successful blocking of the process, 0 otherwise */
int32_t block(int32_t pid);

/* blocks the process pointed by pcb. It is only possible to block a process ready or running (blocking a delayed process would not make much sense). */
int32_t block_pcb(PCB* target);

/* wakes the process pid up.
   Returns 1 in case of success, 0 otherwise.
*/
int32_t wake_up(int32_t pid);

/* wakes the process pointed by pcb up.
   Returns 1 in case of success, 0 otherwise.
*/
int32_t wake_up_pcb(PCB* target);

/* delays the process pid by n ms.
   Returns 1 if the process was sucessfully delayed, 0 otherwise.
*/
int32_t delay(int32_t pid, uint32_t n);

/* delays the process pointed by target during n ms.
   Returns 1 if the process was sucessfully delayed, 0 otherwise. */
int32_t delay_pcb(PCB* target, uint32_t n);

/* Returns the priority of the process pid or -1 if it doesn't exist */
int32_t get_priority(int32_t pid);

/* Copy the program name of the program executed by pid in program_name (up to n characters). Returns 1 in case of success, 0 otherwise. */
int32_t get_program(int32_t pid, char* program_name, int32_t n);

/* returns the state of the process pid or -1 in case of failure */
int32_t get_state(int32_t pid);

/* returns the parent's pid of pid or -2 in case of failure */
int32_t get_parent(int32_t pid);

/* checks if the process pid has terminated. It it has, its return value is copied in status and the process is deleted. Otherwise, the caller is blocked until such an event occurs. Returns 1 if no error happened (being blocked is NOT an error) or 0 otherwise. */
int32_t wait(int32_t pid, int32_t* status);


#endif
